package cn.dansj.utils.project;

import cn.dansj.bean.*;
import cn.dansj.common.mail.bean.MailConfigure;
import cn.dansj.common.mail.bean.MailContent;
import cn.dansj.common.mail.MailUtils;
import cn.dansj.common.utils.exception.TokenParseException;
import cn.dansj.entity.*;
import cn.dansj.exception.InterfaceResponseException;
import cn.dansj.service.*;
import cn.dansj.common.utils.bean.CacheToken;
import cn.dansj.common.utils.bean.RequestToken;
import cn.dansj.common.utils.json.DictMap;
import cn.dansj.common.utils.bean.Return;
import cn.dansj.common.utils.enums.DbType;
import cn.dansj.common.utils.enums.ResponseCode;
import cn.dansj.common.context.ApplicationContextProvider;
import cn.dansj.common.utils.crypt.Pbkdf2Sha256;
import cn.dansj.common.utils.http.HostUtils;
import cn.dansj.common.request.RequestUtils;
import cn.dansj.common.request.RestTemplateUtils;
import cn.dansj.common.utils.http.UrlUtils;
import cn.dansj.common.redis.RedisUtils;
import cn.dansj.common.utils.transfer.*;
import cn.dansj.configure.EncryptAndDecrypt;
import cn.dansj.utils.tencent.LightHouseUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import cn.dansj.utils.minio.MinioUtils;
import cn.dansj.enums.GlobalEnv;
import com.xxl.job.entity.Connection;
import com.xxl.job.entity.XxlJobInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.web.client.HttpClientErrorException;
import jakarta.servlet.http.HttpServletRequest;

import java.io.*;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

//专用工具类
public class ProjectUtils {
    private static final Logger logger = LoggerFactory.getLogger(ProjectUtils.class);
    private static volatile Minio minio;
    private static final ConcurrentHashMap<String, LightHouseUtils> tencentCloudClient = new ConcurrentHashMap<>();

    private final static RedisUtils redisUtils = ApplicationContextProvider.getBean(RedisUtils.class);
    private final static ConnectionService connectionService = ApplicationContextProvider.getBean(ConnectionService.class);
    private final static UserService userService = ApplicationContextProvider.getBean(UserService.class);
    private final static SettingService settingService = ApplicationContextProvider.getBean(SettingService.class);
    private final static RoleService roleService = ApplicationContextProvider.getBean(RoleService.class);
    private final static MailUtils mailUtils = new MailUtils();
    private final static RequestLogService requestLogService = ApplicationContextProvider.getBean(RequestLogService.class);

    public static RedisUtils getRedisUtils() {
        return redisUtils;
    }

    public static final List<String> dbTypes = ArrayUtils.asList(DbType.oracle.name(), DbType.mysql.name(), DbType.sqlServer.name(), DbType.dm.name());

    public static JSONObject getDbLabel() {
        return getDbLabel(false);
    }

    public static JSONObject getDbLabel(boolean active) {
        //定义可选的数据库类型

        //生成空模版{"oracle":[],"mysql":[]}
        JSONObject dbLists = new JSONObject();
        JSONObject db = new JSONObject();
        for (String dbType : dbTypes) {
            dbLists.put(dbType, new ArrayList<String>());
            db.put(dbType, new ArrayList<String>());
        }

        List<Connection> allConnection = connectionService.getConnectionsByDbTypes(dbTypes);
        for (Connection con : allConnection) {
            DbType dbType = DbType.getDbType(con.getDbType());
            JSONObject dbInfo = new JSONObject();
            dbInfo.put("name", con.getName());
            dbInfo.put("sequence", con.getSequence());
            dbInfo.put("status", con.getStatus());
            if (active) {
                if (con.getStatus() == 1) {
                    dbLists.getJSONArray(dbType.name()).add(con.getName());
                    db.getJSONArray(dbType.name()).add(dbInfo);
                }
            } else {
                dbLists.getJSONArray(dbType.name()).add(con.getName());
                db.getJSONArray(dbType.name()).add(dbInfo);
            }
        }

        JSONObject data = new JSONObject();
        data.put("dbTypes", dbTypes);
        data.put("dbLists", dbLists);
        data.put("db", db);

        return data;
    }

    public static String getRequestTokenID() {
        HttpServletRequest servletRequest = RequestUtils.getRequest();
        return Verification.checkNull(servletRequest.getAttribute("x-token")) ? "" : servletRequest.getAttribute("x-token").toString();
    }

    public static RequestToken parseRequestToken() {
        HttpServletRequest servletRequest = RequestUtils.getRequest();

        try {
            String encryptToken = servletRequest.getHeader("x-token");
            String decryptToken = decryptByPrivateKey(encryptToken);
            RequestToken requestToken = DictMap.parseObject(decryptToken, RequestToken.class);
            servletRequest.setAttribute("x-token", Verification.checkNotNull(requestToken) ? requestToken.getToken() : "");
            return requestToken;
        } catch (Exception exception) {
            throw new TokenParseException("无访问权限");
        }
    }

    public static CacheToken getCacheToken() {
        return getCacheToken(getRequestTokenID());
    }

    public static CacheToken getCacheToken(String tokenId) {
        try {
            if (Verification.checkNull(tokenId)) return new CacheToken();
            Map<Object, Object> token = redisUtils.hgetall(tokenId.startsWith(GlobalEnv.CacheTokenPrefix) ? tokenId : GlobalEnv.CacheTokenPrefix + tokenId);
            if (Verification.checkNull(token)) return new CacheToken();
            return DictMap.parseObject(token, CacheToken.class);
        } catch (Exception exception) {
            logger.error(exception.getMessage());
            return new CacheToken();
        }
    }

    public static void updateCacheToken() {
        CacheToken cacheToken = CurrentThreadLocalUserInfo.getCurrentThreadLocalUserInfo().getCacheToken();
        if (getProjectDefaultBaseToken().equals(cacheToken.getUuid())) return;
        cacheToken.setLastActiveTime(System.currentTimeMillis());
        redisUtils.hmset(getRequestTokenID(), cacheToken.toJSONObject(), cacheToken.getExpireTime().toDate());
    }

    public static void delAllCacheToken() {
        redisUtils.delete(redisUtils.keys(GlobalEnv.CacheTokenPrefix + "*"));
    }

    public static void delCacheToken(String username, String role, boolean checkPlatform) {
        List<String> TokenKeys = redisUtils.keys(GlobalEnv.CacheTokenPrefix + "*");
        Set<String> delToken = new HashSet<>();
        final Object requestPlatform = RequestUtils.getRequest().getAttribute("platform");
        final String platform = Verification.checkNotNull(requestPlatform) ? requestPlatform.toString() : parseRequestToken().getPlatform();
        for (String tokenId : TokenKeys) {
            CacheToken cacheToken = DictMap.parseObject(redisUtils.hgetall(tokenId), CacheToken.class);
            if (cacheToken == null) {
                continue;
            }

            boolean checkUser = (Verification.checkNotNull(username) && username.equals(cacheToken.getUsername())) || (Verification.checkNotNull(role) && role.equals(cacheToken.getRole()));

            if (checkPlatform) {
                if (checkUser && Boolean.valueOf(checkMobileUserAgent()).equals(cacheToken.isMobile()) && platform.equals(Transformation.nvl(cacheToken.getPlatform(), ""))) {
                    delToken.add(tokenId);
                }
            } else {
                if (checkUser) {
                    delToken.add(tokenId);
                }
            }
        }
        redisUtils.delete(new ArrayList<>(delToken));
    }

    public static void addCacheToken(CacheToken cacheToken) {
        redisUtils.hmset(GlobalEnv.CacheTokenPrefix + cacheToken.getUuid(), cacheToken.toJSONObject(), cacheToken.getExpireTime().toDate());
    }

    public static boolean resetSequence(String name) {
        return resetSequence(name, -1);
    }

    public static boolean resetSequence(String name, long sequence) {
        return redisUtils.set(formatSequenceName(name), String.valueOf(sequence));
    }

    private static String formatSequenceName(String name) {
        return "sequence@" + name;
    }

    public static Long getSequence(String name) {
        return redisUtils.increment(formatSequenceName(name), 1000, 1);
    }

    public static CacheToken createToken(User user) {
        boolean mobileUserAgent = checkMobileUserAgent();
        //设置token的有效期
        Calendar instance = Calendar.getInstance();
        instance.setTime(new Date());
        JSONObject session = getSettingByRedis("session");
        int tokenExpireDays = Transformation.nvl(mobileUserAgent ? session.getInteger("base.token.mobile.expire.days") : session.getInteger("base.token.expire.days"), GlobalEnv.TokenExpireDays);
        if (tokenExpireDays <= 0) tokenExpireDays = GlobalEnv.TokenExpireDays;

        instance.add(Calendar.DATE, tokenExpireDays);

        //生成token信息
        CacheToken cacheToken = new CacheToken(user.getUsername(), user.getRole(), user.getSequence(), mobileUserAgent);
        cacheToken.setPlatform(Transformation.nvl(RequestUtils.getRequest().getAttribute("platform"), "").toString());
        cacheToken.setAllowLoginFrom(user.getLoginFrom());
        cacheToken.setExpireTime(instance.getTime().getTime());
        cacheToken.setApiToken(user.getToken());

        if (user.getUniqueLogin() == 1) delCacheToken(cacheToken.getUsername(), null, true);
        addCacheToken(cacheToken);
        return cacheToken;
    }

    //初始化用户数据
    public static Return<?> initUserLoginData(User user) {
        Object token = encryptByPrivateKey(createToken(user).getUuid());
        Object role = encryptByPrivateKey(user.getRole());
        Object username = encryptByPrivateKey(user.getUsername());
        Object sequence = encryptByPrivateKey(user.getSequence());
        return Return.T(DictMap.of("token", token, "role", role, "username", username, "sequence", sequence));
    }

    public static Return<User> checkLoginfo(String username, String password) {
        User user = userService.getUser(username);
        boolean check;
        // mysql查询时存在值不区分大小写的情况, 再次校验用户名是否和库里存的一致
        if (user == null || !user.getUsername().equals(username)) {
            check = false;
        } else {
            boolean mobileUserAgent = checkMobileUserAgent();

            String roleStatus = roleService.getRoleByName(user.getRole()).getStatus();
            if ("0".equals(user.getStatus()) || "0".equals(roleStatus)) {
                return Return.T(false, "该用户已被禁止登录");
            } else if (user.getLoginFrom() == 1 && !mobileUserAgent) {
                return Return.T(false, "当前用户只允许手机端登录");
            } else if (user.getLoginFrom() == 2 && mobileUserAgent) {
                return Return.T(false, "当前用户只允许电脑端登录");
            }

            check = Pbkdf2Sha256.verification(password, user.getPassword());
        }
        return Return.T(check ? user : null).setMessage(check ? "认证成功" : "认证失败").setStatus(check);
    }

    public static String transferMenuPermission(String permission) {
        if (permission.startsWith("[")) {
            final List<String> splitPermission = JSONObject.parseObject(permission, new TypeReference<>() {
            });
            return StringUtils.replace(String.join(",", splitPermission), "\"", "");
        } else {
            return StringUtils.replace(permission, "\"", "");
        }
    }

    public static DictMap getSettingByRedis(String key) {
        String keyInDB = StringUtils.replace(key, "system.setting@", "");
        String keyInRedis = "system.setting@" + keyInDB;

        Map<Object, Object> settingMap = redisUtils.hgetall(keyInRedis);
        DictMap result = DictMap.of();
        if (Verification.checkNull(settingMap)) {
            List<Setting> allByClassCode = settingService.getAllByClassCode(keyInDB);
            settingMap.put("cache", allByClassCode);
            settingMap.put("date", DateTime.getInstance().stringTimestamp());
            redisUtils.hmset(keyInRedis, settingMap);
        }

        if (Verification.checkNotNull(settingMap)) {
            List<Setting> settings = DictMap.parseObject(Transformation.toJSONString(settingMap.get("cache")), new TypeReference<>() {
            });

            if (settings != null) {
                result.put("cacheDate", settingMap.get("date"));
                for (Setting setting : settings) {
                    result.put(setting.getName(), setting.getValue());
                }
            }
        }

        return result;
    }

    public static <T> T getSettingByRedis(String key, String name, T defaultValue) {
        try {
            Object obj = getSettingByRedis(key).getOrDefault(name, defaultValue);
            if (defaultValue.getClass().equals(Boolean.class)) {
                obj = Transformation.castToBoolean(obj);
            }
            return JSONObject.parseObject(Transformation.isNumeric(obj.toString()) ? String.valueOf(obj) : JSON.toJSONString(obj), new TypeReference<>() {
            });
        } catch (Exception exception) {
            logger.error(exception.getMessage());
            return defaultValue;
        }
    }

    public static MailConfigure getMailConfig() {
        JSONObject mailConfig = getSettingByRedis("mail");
        if (Verification.checkNull(mailConfig)) return null;
        String host = mailConfig.getString("host");
        String username = mailConfig.getString("username");
        boolean isSSL = Transformation.nvl(mailConfig.getBooleanValue("isSSL"), false);
        int port = Transformation.nvl(mailConfig.getInteger("port"), 25);
        String password = mailConfig.getString("password");

        if (Verification.checkAnyNull(host, username, password)) return null;

        MailConfigure configure = new MailConfigure();
        configure.setHost(host);
        configure.setUsername(username);
        configure.setSsl(isSSL);
        configure.setPort(port);
        configure.setPassword(password);
        return configure;
    }

    public static void sendCaptcha(String recipient, String code, String expire) {
        GlobalEnv.threadPool.execute(() -> {
            try {
                MailConfigure mailConfig = getMailConfig();

                mailUtils.setConfig(mailConfig);
                MailContent mailContent = new MailContent();
                mailContent.setRecipient(recipient);
                mailContent.setSubject("验证码");
                mailContent.setTemplate("captcha.html");
                mailContent.setContext("code", code, "expire", expire);
                mailUtils.send(mailContent);
            } catch (Exception ignore) {
            }
        });
    }

    public static String checkAuth() {
        String checkInfo = "";
        // 解析header中的token信息, 并重新保存到request中以备后续使用
        RequestToken requestToken;
        try {
            requestToken = CurrentThreadLocalUserInfo.getCurrentThreadLocalUserInfo().getRequestToken();
            AssertUtils.notNull(requestToken, new TokenParseException("token无效, 请重新登录"));
        } catch (TokenParseException e) {
            return e.getMessage();
        }

        JSONObject settings = getSettingByRedis("session");
        int TokenNotOperatedMinutes = Transformation.nvl(settings.getInteger("base.token.not.operate.minute"), GlobalEnv.TokenNotOperatedMinutes);
        int EncryptTokenExpireSeconds = Transformation.nvl(settings.getInteger("encrypt.token.expire.second"), GlobalEnv.EncryptTokenExpireSeconds);

        HttpServletRequest request = RequestUtils.getRequest();

        //加密的token有效期
        if (System.currentTimeMillis() - requestToken.getTokenTimestamp() > TimeUnit.SECONDS.toMillis(EncryptTokenExpireSeconds)) {
            checkInfo = "Token已过期, 请重新登陆";
        }

        // 默认token只校验加密时间, 通过直接放行
        if (getProjectDefaultBaseToken().equals(requestToken.getTokenUid()) && Verification.checkNull(checkInfo)) {
            request.setAttribute(GlobalEnv.WithProjectToken, Verification.checkNull(checkInfo));
            return checkInfo;
        }

        boolean mobileUserAgent = checkMobileUserAgent();

        try {
            if (Verification.checkNull(checkInfo)) {
                CacheToken cacheToken = CurrentThreadLocalUserInfo.getCurrentThreadLocalUserInfo().getCacheToken();
                //token解析成功
                if (Verification.checkAnyNull(cacheToken, cacheToken.getUsername(), cacheToken.getSequence(), cacheToken.getRole())) {
                    checkInfo = "登录状态失效, 请重新登录";
                }

                //校验baseToken的过期时间
                else if (System.currentTimeMillis() > cacheToken.getExpireTime().toDate().getTime()) {
                    checkInfo = "token已失效, 请重新登录";
                }

                //静默时间限制
                else if ((System.currentTimeMillis() - cacheToken.getLastActiveTime().toDate().getTime()) > TimeUnit.MINUTES.toMillis(TokenNotOperatedMinutes) && !mobileUserAgent) {
                    checkInfo = "长时间未操作, 登陆状态过期";
                }
                /* 目前只有console需要认证菜单权限, 暂时取消菜单权限认证
                else if ("console".equals(requestToken.getPlatform()) && !getRequest().getRequestURI().startsWith(InterfaceConstant.ForwardPrefix)
                        && !"/".equals(requestToken.getCurrentPath()) && !cacheToken.getAllowVisit().contains(requestToken.getCurrentPath())
                        && !mobileUserAgent) {
                    checkInfo = "当前菜单无权限访问";
                }**/
                else if (cacheToken.getAllowLoginFrom() == 1 && !mobileUserAgent) {
                    checkInfo = "当前用户只允许手机端登录";
                } else if (cacheToken.getAllowLoginFrom() == 2 && mobileUserAgent) {
                    checkInfo = "当前用户只允许电脑端登录";
                }
            }
        } catch (Exception exception) {
            checkInfo = "无效的Token信息";
        }

        if (Verification.checkNull(checkInfo)) {
            updateCacheToken();
        }

        //校验token通过验证
        request.setAttribute(GlobalEnv.WithProjectToken, Verification.checkNull(checkInfo));
        return checkInfo;
    }

    public static boolean checkMobileUserAgent() {
        HttpServletRequest request = RequestUtils.getRequest();
        if ("0".equals(request.getAttribute("checkMobile"))) return false;

        String userAgent = request.getHeader("user-agent").toLowerCase();
        return userAgent.contains("android") || userAgent.contains("iphone") || userAgent.contains("ipad") || userAgent.contains("mobile");
    }

    public static void checkUserInterfaceTokenInfo() {
        HttpServletRequest request = RequestUtils.getRequest();
        //获取请求中的token信息
        String token = Transformation.nvl(request.getHeader("token"), RequestUtils.getRequestParams().getString("token"), "");
        if (Verification.checkNotNull(token) && DictMap.parseObject(redisUtils.hget("interfaceToken", token), User.class) == null) {
            throw new InterfaceResponseException(ResponseCode.TokenError);
        }
    }

    public static User getUserInterfaceTokenInfo() {
        HttpServletRequest request = RequestUtils.getRequest();
        //获取请求中的token信息
        return getUserInterfaceTokenInfo(Transformation.nvl(request.getHeader("token"), RequestUtils.getRequestParams().getString("token"), ""));
    }

    public static User getUserInterfaceTokenInfo(String token) {
        //token不能为空
        if (Verification.checkNull(token)) return new User();
        //在redis中获取token信息
        User user = DictMap.parseObject(redisUtils.hget("interfaceToken", token), User.class);
        // checkPass = castToBoolean(user.getStatus());
        if (Verification.checkNull(user) || Verification.checkNull(user.getSequence())) {
            // 数据库查询token信息, 后续更新token_info的时候要顺带更新redis的信息
            user = userService.getUserByToken(token);
            // 查询结果为空也放进redis,防止一直查数据库,后续有值找地方更新
            updateUserInterfaceTokenInfo(token, user);
        }

        return user == null ? new User() : user;
    }

    public static List<Map<String, Integer>> getPointInfo() {
        int lv = 1;
        int activeDay = 0;
        List<Map<String, Integer>> activeDays = new ArrayList<>();
        while (lv < 10) {
            int increase = (lv + 5) * lv;
            activeDay += increase;
            Map<String, Integer> map = new HashMap<>();
            map.put("lv", lv);
            map.put("point", activeDay);
            activeDays.add(map);
            lv++;
        }
        return activeDays;
    }

    public static LimitReturn getLimit(int point) {
        int lv = 1;

        for (Map<String, Integer> activeDay : getPointInfo()) {
            if (point <= activeDay.get("point")) {

                break;
            }
            lv = activeDay.get("lv") + 1;
        }

        return new LimitReturn(1, lv, lv * 100, lv);
    }

    // 新建用户，修改用户积分、状态等信息需要调用此方法
    public static void updateUserInterfaceTokenInfo(String token, User user) {
        if (Verification.checkNull(token)) return;
        redisUtils.hset("interfaceToken", token, user == null ? null : user.setPassword(null));
    }

    public static void updateUserInterfaceTokenInfo(User user) {
        updateUserInterfaceTokenInfo(user.getToken(), user);
    }

    public static List<String> initInterfaceVip() {
        List<String> interfaceVip = roleService.getInterfaceVip();
        redisUtils.hset("interfaceVip", "cache", interfaceVip);
        return interfaceVip;
    }

    public static boolean checkInterfaceVip(String role) {
        List<String> vip = JSONObject.parseObject(Transformation.toString(redisUtils.hget("interfaceVip", "cache")), new TypeReference<>() {
        });
        if (vip == null) {
            vip = initInterfaceVip();
        }
        return vip.contains(role);
    }

    // 获取项目基础token, 为空取全局变量里面的
    public static String getProjectDefaultBaseToken() {
        JSONObject session = getSettingByRedis("session");
        return Transformation.nvl(session.getString("project.default.base.token"), GlobalEnv.ProjectDefaultBaseToken);
    }

    // 获取指定长度的数字
    public static String generateRandomNumber(int length) {
        Random random = new Random();
        StringBuilder number = new StringBuilder();

        for (int i = 0; i < length; i++) {
            // 第一位生成1到9,后续位数生成0到9之间的随机数
            int digit = i == 0 ? RandomUtils.randomInt(1, 9) : random.nextInt(10);
            number.append(digit);
        }

        return number.toString();
    }

    public static User getEmptyUser() {
        User user = new User();
        user.setSequence(RandomUtils.sequence());
        user.setUsername("user" + user.getSequence());
        user.setRole("default_user");
        user.setStatus("1");
        user.setPayCount(0);
        user.setPoint(0);
        user.setUniqueLogin(0);
        user.setLoginFrom(0);
        user.setCrtTime(GetTime.dbTimestamp());
        user.setUpdateTime(user.getCrtTime());
        user.setToken(RandomUtils.getUid());
        return user;
    }

    public static MinioUtils getMinio() {
        JSONObject settingByRedis = getSettingByRedis("minio");
        String endpoint = settingByRedis.getString("minio.endpoint");
        String accessKey = settingByRedis.getString("minio.accessKey");
        String secretKey = settingByRedis.getString("minio.secretKey");
        String md5 = Md5Utils.md5(settingByRedis.getString("cacheDate"));

        if (minio == null || !md5.equals(minio.getMd5())) {
            synchronized (ProjectUtils.class) {
                if (minio == null || !md5.equals(minio.getMd5())) {
                    logger.info("create new minioUtils");
                    MinioUtils minioUtils = MinioUtils.getInstance(endpoint, accessKey, secretKey);
                    minio = new Minio(md5, minioUtils);
                }
            }
        }
        return minio.getMinioUtils();
    }

    private static boolean needUpdateTencentCloudClient(String client, DictMap clientInfo) {
        return Verification.checkNull(tencentCloudClient.get(client)) || !tencentCloudClient.get(client).checkMd5(clientInfo.getString("secretId"), clientInfo.getString("secretKey"), clientInfo.getString("region"));
    }

    private static DictMap getTencentClientInfo() {
        return getSettingByRedis("tencent.cloud").filter(e -> e.getKey().startsWith("account."), e -> e.replaceFirst("account.", ""));
    }

    public static LightHouseUtils getTencentCloudClient(String client) {
        final DictMap clientInfo = getTencentClientInfo().getJSONObject(client);
        AssertUtils.notNull(client, new InterfaceResponseException(550, "实例不存在"));
        if (needUpdateTencentCloudClient(client, clientInfo)) {
            synchronized (ProjectUtils.class) {
                if (needUpdateTencentCloudClient(client, clientInfo)) {
                    logger.info("update new lightHouseUtils => {}", client);
                    String secretId = clientInfo.getString("secretId");
                    String secretKey = clientInfo.getString("secretKey");
                    String region = clientInfo.getString("region");
                    LightHouseUtils lightHouseUtils = new LightHouseUtils(secretId, secretKey, region);
                    tencentCloudClient.put(client, lightHouseUtils);
                }
            }
        }
        return tencentCloudClient.get(client);
    }

    public static List<String> getTencentCloudInstance() {
        return ArrayUtils.asList(getTencentClientInfo().keySet());
    }

    record Minio(String md5, MinioUtils minioUtils) {

        public String getMd5() {
            return md5;
        }

        public MinioUtils getMinioUtils() {
            return minioUtils;
        }
    }

    public static void backUpTask(final XxlJobInfo xxlJobInfo) {
        GlobalEnv.threadPool.execute(() -> {
            String md5 = Md5Utils.md5((xxlJobInfo.getGlueSource() + xxlJobInfo.getGlueParams()).replaceAll("\\s+", ""));
            String name = xxlJobInfo.getId() + "@" + xxlJobInfo.getJobDesc() + "@" + md5;
            writeToTxtUploadToMinio("task", name, xxlJobInfo.toJSONString());
        });
    }

    public static void backUpInterfaceInfo(final InterfaceInfo interfaceInfo) {
        GlobalEnv.threadPool.execute(() -> {
            String md5 = Md5Utils.md5(interfaceInfo.getCode()).replaceAll("\\s+", "");
            String name = interfaceInfo.getSequence() + "@" + interfaceInfo.getInterfaceName() + "@" + md5;
            writeToTxtUploadToMinio("api", name, interfaceInfo.toJSONString());
        });
    }

    @SuppressWarnings("all")
    private static boolean writeToTxtUploadToMinio(String bucket, String filename, String content) {
        File file = new File(System.getProperty("java.io.tmpdir") + filename + ".txt");

        try {
            MinioUtils minioUtils = getMinio();

            try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file))) {
                bufferedWriter.write(content);
            }
            minioUtils.upload(bucket, file);

            return true;
        } catch (Exception e) {
            return false;
        } finally {
            file.delete();
        }
    }

    public static MediaType mediaType(String filename) {
        return MediaTypeFactory.getMediaType(filename).orElse(MediaType.APPLICATION_OCTET_STREAM);
    }

    public static String getBucketName(String bucketName) {
        return GlobalEnv.MinioKeyPrefix + bucketName;
    }

    public static JSONObject getRealIpInfo() {
        final String ip = RequestUtils.getRealIp();
        boolean isPrivateIpAddress = HostUtils.isPrivateIpAddress(ip);
        JSONObject ipAddress = DictMap.of();
        if (!isPrivateIpAddress) {
            ipAddress = DictMap.parseObject(Transformation.toJSONString(redisUtils.hget("ipAddress", ip)));
            if (Verification.checkNull(ipAddress) || !"Success".equals(ipAddress.getString("code"))) {
                try {
                    ipAddress = RestTemplateUtils.builder().retryTimes(0).build().get("https://qifu-api.baidubce.com/ip/geo/v1/district?ip=" + ip, JSONObject.class);
                } catch (HttpClientErrorException httpClientErrorException) {
                    ipAddress = DictMap.parseObject(httpClientErrorException.getResponseBodyAsString());
                } catch (Exception exception) {
                    logger.error("ip:{} 获取地址信息失败", ip, exception);
                }
                redisUtils.hset("ipAddress", ip, ipAddress);
            }
        }
        ipAddress.put("ip", ip);
        ipAddress.put("isPrivateIpAddress", isPrivateIpAddress);
        return ipAddress;
    }

    public static String getRequestUrl() {
        final HttpServletRequest request = RequestUtils.getRequest();
        return "<" + request.getMethod() + ">" + request.getRequestURL().toString() + UrlUtils.joinParams(request.getParameterMap());
    }

    public static FrpConnectInfo getFrpConnectInfo(String server) {
        final DictMap frp = getSettingByRedis("frp");
        final List<DictMap> frps = JSON.parseArray(frp.getString("frps.server.info"), DictMap.class);
        final List<DictMap> frpc = JSON.parseArray(frp.getString("frpc.server.info"), DictMap.class);

        final List<DictMap> frpInfo = ArrayUtils.asList(frpc, frps);

        for (DictMap dictMap : frpInfo) {
            if (dictMap.getString("server").equals(server)) {
                final HttpHeaders headers = new HttpHeaders();
                String username = dictMap.getString("username");
                String password = dictMap.getString("password");
                headers.setBasicAuth(username, password);
                headers.set("referer", "https://frp.dansj.cn");

                return new FrpConnectInfo(headers, UrlUtils.formatUrl(dictMap.getString("url")));
            }
        }

        throw new IllegalArgumentException("找不到连接信息");
    }

    public static void recordRequestLog(HttpServletRequest request, int responseCode, String message, String controller, boolean interfaceApi) {
        final boolean logEnable = getSettingByRedis("system", interfaceApi ? "log.interface.enable" : "log.system.enable", false);
        if (!logEnable) return;
        RequestLog requestLog = new RequestLog();
        requestLog.setMethod(request.getMethod());
        requestLog.setUrl(request.getRequestURI());
        requestLog.setController(controller);
        requestLog.setSequence(RandomUtils.sequence());
        requestLog.setResponseStartTime(new Timestamp(System.currentTimeMillis()));
        DictMap params = RequestUtils.getMaybeMultipartRequestParams();
        requestLog.setParams(params);
        requestLog.setUserAgent(request.getHeader("user-agent"));

        String token = Transformation.nvl(request.getHeader("token"), params.getString("token"), "");
        requestLog.setToken(token);

        requestLog.setLoginUser(requestLog.getUrl().equalsIgnoreCase("/login") ? params.getString("username") : getCacheToken().getUsername());
        requestLog.setStatus(responseCode == 200 ? 1 : 0);
        requestLog.setResponseCode(String.valueOf(responseCode));
        requestLog.setFullException(message);
        requestLog.setSketch(message);
        requestLog.setResponseEndTime(new Timestamp(System.currentTimeMillis()));

        requestLog.setCrtTime(new Timestamp(System.currentTimeMillis()));
        requestLog.setCost(requestLog.getResponseEndTime().getTime() - requestLog.getResponseStartTime().getTime());
        requestLog.setIpInfo(getRealIpInfo().toJSONString());
        GlobalEnv.threadPool.execute(() -> requestLogService.save(requestLog));
    }

    public static boolean isApiPrefix() {
        return RequestUtils.getRequest().getRequestURI().matches("^(?:/api|api/|api)$|^(?:/api|api)/(.*?)");
    }

    private static final EncryptAndDecrypt eda = ApplicationContextProvider.getBean(EncryptAndDecrypt.class);

    public static String decryptByPrivateKey(String data) {
        return eda.decrypt(data);
    }

    public static String encryptByPrivateKey(String data) {
        return eda.encrypt(data);
    }
}